<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="x0NFL" id="x0NFL"><span data-lake-id="ua5837b9d" id="ua5837b9d">典型回答</span></h1>
  <p data-lake-id="uc13452e4" id="uc13452e4"><br></p>
  <p data-lake-id="u0ebc4593" id="u0ebc4593"><span data-lake-id="u86a56ae8" id="u86a56ae8">基于Open JDK 8中源码显示，synchronized升级过程中有2次自旋。</span></p>
  <p data-lake-id="u4725d1fd" id="u4725d1fd"><span data-lake-id="u3a4f7111" id="u3a4f7111">​</span><br></p>
  <p data-lake-id="u9ac84509" id="u9ac84509"><strong><span data-lake-id="uadf0f0a9" id="uadf0f0a9">第一次自旋</span></strong></p>
  <p data-lake-id="u03c7b43b" id="u03c7b43b"><span data-lake-id="uf1229cbb" id="uf1229cbb">第一次自旋发生在 synchronized 获取轻量级锁时，即当一个线程尝试获取一个被其他线程持有的轻量级锁时，它会自旋等待锁的持有者释放锁。</span></p>
  <p data-lake-id="uef22bb70" id="uef22bb70"><span data-lake-id="u9058e2d7" id="u9058e2d7">​</span><br></p>
  <p data-lake-id="u9d601c8f" id="u9d601c8f"><span data-lake-id="ue7cac594" id="ue7cac594">在OpenJDK 8中，轻量级锁的自旋默认是开启的，最多自旋15次，每次自旋的时间逐渐延长。如果15次自旋后仍然没有获取到锁，就会升级为重量级锁。</span></p>
  <p data-lake-id="u1cd3952d" id="u1cd3952d"><span data-lake-id="u837b7550" id="u837b7550">​</span><br></p>
  <p data-lake-id="u91ad651f" id="u91ad651f"><span data-lake-id="u9bb5ee72" id="u9bb5ee72">代码的具体实现在 ObjectSynchronizer::fast_enter() 函数中，有如下代码：</span></p>
  <p data-lake-id="u6eba73be" id="u6eba73be"><span data-lake-id="u5bc1287f" id="u5bc1287f">​</span><br></p>
  <pre lang="java"><code>
// try to obtain the lock by a fast-path CAS
if (Atomic::cmpxchg_ptr(lock, obj-&gt;mark_addr(), mark) == mark) {
  TEVENT (fast_enter: release stacklock) ;
  return;
}

</code></pre>
  <p data-lake-id="u302ef536" id="u302ef536"><span data-lake-id="ud13f0afd" id="ud13f0afd">​</span><br></p>
  <p data-lake-id="ub3eb834c" id="ub3eb834c"><span data-lake-id="u342ba5a2" id="u342ba5a2">如果 CAS 操作成功，则表示获取锁成功，函数直接返回。否则，进入自旋。</span></p>
  <p data-lake-id="u9ffc5846" id="u9ffc5846"><span data-lake-id="ucc05ca63" id="ucc05ca63">​</span><br></p>
  <p data-lake-id="u27c62fd9" id="u27c62fd9"><strong><span data-lake-id="ub8942898" id="ub8942898">第二次自旋</span></strong></p>
  <p data-lake-id="uae720a48" id="uae720a48"><span data-lake-id="u2ed12773" id="u2ed12773">第二次自旋发生在 synchronized 轻量级锁升级到重量级锁的过程中。即当一个线程尝试获取一个被其他线程持有的重量级锁时，它会自旋等待锁的持有者释放锁。</span></p>
  <p data-lake-id="u450b87f1" id="u450b87f1"><span data-lake-id="ud8720745" id="ud8720745">​</span><br></p>
  <p data-lake-id="u91afd4ce" id="u91afd4ce"><span data-lake-id="u91425c0a" id="u91425c0a">在OpenJDK 8中，默认情况下不会开启重量级锁自旋。如果线程在尝试获取重量级锁时，发现该锁已经被其他线程占用，那么线程会直接阻塞，等待锁被释放。如果锁被持有时间很短，可以考虑开启重量级锁自旋，避免线程挂起和恢复带来的性能损失。</span></p>
  <p data-lake-id="u9941e705" id="u9941e705"><span data-lake-id="ubdb194ac" id="ubdb194ac">​</span><br></p>
  <p data-lake-id="u9645038a" id="u9645038a"><span data-lake-id="u3eb09203" id="u3eb09203">第二次自旋的代码实现在 ObjectSynchronizer::slow_enter() 函数中，有如下代码：</span></p>
  <p data-lake-id="u157dc48a" id="u157dc48a"><span data-lake-id="u4384d2e3" id="u4384d2e3">​</span><br></p>
  <pre lang="java"><code>
// Anticipate successful CAS -- the ST of the displaced mark must
// be visible &lt;= the ST performed by the CAS.
lock-&gt;set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj-&gt;mark_addr(), mark)) {
  TEVENT (slow_enter: release stacklock) ;
  return ;
}

</code></pre>
  <p data-lake-id="u63a5d81a" id="u63a5d81a"><br></p>
  <p data-lake-id="uf8e32927" id="uf8e32927"><strong><span data-lake-id="u61959a5b" id="u61959a5b">自适应自旋</span></strong></p>
  <p data-lake-id="u0f799a68" id="u0f799a68"><span data-lake-id="u9e1a55b3" id="u9e1a55b3">在JDK6中之后的版本中，JVM引入了</span><strong><span data-lake-id="ue6403bc7" id="ue6403bc7">自适应的自旋</span></strong><span data-lake-id="ud1ef78d3" id="ud1ef78d3">机制。该机制通过监控轻量级锁自旋等待的情况，动态调整自旋等待的时间。</span></p>
  <p data-lake-id="u7018ca3f" id="u7018ca3f"><span data-lake-id="u20c20c26" id="u20c20c26">​</span><br></p>
  <p data-lake-id="uc14f05cd" id="uc14f05cd"><span data-lake-id="u4f419451" id="u4f419451">如果自旋等待的时间很短，说明锁的竞争不激烈，当前线程可以自旋等待一段时间，避免线程挂起和恢复带来的性能损失。如果自旋等待的时间较长，说明锁的竞争比较激烈，当前线程应该及时释放CPU资源，让其他线程有机会执行。</span></p>
  <p data-lake-id="u0b265187" id="u0b265187"><span data-lake-id="u92ae46a8" id="u92ae46a8">​</span><br></p>
  <p data-lake-id="u8e57789c" id="u8e57789c"><span data-lake-id="u307459ff" id="u307459ff">自适应的自旋实现在ObjectSynchronizer::FastHashCode()函数中。该函数会根据轻量级锁自旋等待的情况，调整自旋等待的时间。</span></p>
  <p data-lake-id="u57f698be" id="u57f698be"><span data-lake-id="ua1b958c9" id="ua1b958c9">​</span><br></p>
  <h1 data-lake-id="X6XiE" id="X6XiE"><span data-lake-id="ub38a99ad" id="ub38a99ad">扩展知识</span></h1>
  <p data-lake-id="u1f6e0308" id="u1f6e0308"><br></p>
  <h2 data-lake-id="refTU" id="refTU"><span data-lake-id="u337747e8" id="u337747e8">自旋的实现</span></h2>
  <p data-lake-id="ua827c4d1" id="ua827c4d1"><br></p>
  <p data-lake-id="ubf340f8b" id="ubf340f8b"><span data-lake-id="uacecf0af" id="uacecf0af">在OpenJDK 8的源码中，synchronized的升级过程中涉及到了多次自旋操作，其中包括：</span></p>
  <p data-lake-id="uaba7949b" id="uaba7949b"><span data-lake-id="uce29ac00" id="uce29ac00">​</span><br></p>
  <ol list="u5683580b">
   <li fid="u575523c8" data-lake-id="u22fcc1f7" id="u22fcc1f7"><span data-lake-id="ufe418f2d" id="ufe418f2d">第一次自旋：在尝试获取轻量级锁失败后，线程会进行自旋，使用CAS操作去尝试获取锁。这里的自旋并没有使用while循环，而是使用了C++的inline函数，如ObjectSynchronizer::FastLock()。</span></li>
  </ol>
  <p data-lake-id="uebce5c7b" id="uebce5c7b"><br></p>
  <ol list="u5683580b" start="2">
   <li fid="u575523c8" data-lake-id="ua35b6ed9" id="ua35b6ed9"><span data-lake-id="u49ddf5a1" id="u49ddf5a1">第二次自旋：在尝试获取重量级锁失败后，线程会进行自旋，等待拥有锁的线程释放锁。这里的自旋同样使用了C++的inline函数，如ObjectSynchronizer::FastUnlock()。</span></li>
  </ol>
  <p data-lake-id="u6e2e5050" id="u6e2e5050"><br></p>
  <ol list="u5683580b" start="3">
   <li fid="u575523c8" data-lake-id="u05a631b2" id="u05a631b2"><span data-lake-id="u43f46809" id="u43f46809">自适应自旋：在尝试获取轻量级锁时，线程会进行自旋，等待拥有锁的线程释放锁。但这里的自旋不是固定次数的，而是根据前一次自旋的时间和成功获取锁的概率进行自适应调整。这里的自旋实现在C++的Thread.inline.hpp中，如Thread::SpinPause()。</span></li>
  </ol>
  <p data-lake-id="ub66e7b46" id="ub66e7b46"><br></p>
  <p data-lake-id="u794e5e54" id="u794e5e54"><span data-lake-id="uc0c04361" id="uc0c04361">需要注意的是，虽然这些自旋操作并没有使用while循环实现，但其本质上都是在不断尝试获取锁或等待锁的过程中循环执行的。这些循环操作使用的是各种内建函数、指令集和C++的语法特性实现，能够更高效地执行自旋操作，从而提升锁的性能。</span></p>
 </body>
</html>